package server;

import game.Player;
import game.resources.Profile;
import game.resources.ReverseIntegerComparator;

import java.sql.Date;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;


public class profiledGame {
	
	List<Profile> profileList = new ArrayList<Profile>();
	DBLogic DBL;
	Map<Integer,List<Player>> finalStandings;
	
	public static void updateProfilesRanking(Map<Integer,List<Player>> finalStandings) {
		profiledGame proGame = new profiledGame(finalStandings);
		proGame.updateProfiles();
	}
	
	private profiledGame(Map<Integer,List<Player>> finalStandings) {
		DBL = new DBLogic();
		DBL.InitDBConnection();
		this.finalStandings = finalStandings;
		queryProfiles(finalStandings.values());
	}
	
	private void queryProfiles(Collection<List<Player>> PlayersLists) {
	    for (List<Player> players : PlayersLists) {
			for (Player player : players) {
				Profile profile;
				try {
					long userID = player.getUserID();
					profile = DBL.queryProfile(userID);
					if (profile != null)
						profileList.add(profile);
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	public void updateProfiles() {
		Integer[] listOfMoney = new Integer[6];
		
		finalStandings.keySet().toArray(listOfMoney);
		
		List<Integer> ratingsBeforeUpdate = new ArrayList<Integer>();
		for (Profile p : profileList) {
		    ratingsBeforeUpdate.add(p.getRating());
		}
		updateRanking(profileList, finalStandings);
		List<Integer> ratingsAfterUpdate = new ArrayList<Integer>();
		for (Profile p : profileList) {
		    ratingsAfterUpdate.add(p.getRating());
		}
		
		Date gameDate = new Date(System.currentTimeMillis());
		for (Profile profile : profileList) {
		    List<Profile> rivalProfiles = new ArrayList<Profile>(profileList);
		    rivalProfiles.remove(profile);
		    List<String> rivalNames = new ArrayList<String>();
		    for (Profile p : rivalProfiles) {
		        rivalNames.add(p.getFullName());
		    }
		    
		    int delta = ratingsAfterUpdate.get(0) - ratingsBeforeUpdate.get(0);		
		    ratingsAfterUpdate.remove(0);
		    ratingsBeforeUpdate.remove(0);

		    profile.addHistoryEntry(gameDate, rivalNames, delta);
		    DBL.updateProfile(profile);
		}

		Map<Integer, List<Profile>> newHallOfFame;
		try {
		    newHallOfFame = updateHallOfFame(DBL.getHallOfFame(), finalStandings);
		    DBL.updateHallOfFame(newHallOfFame);
		} catch (SQLException e2) {
		    e2.printStackTrace();
		}	
		
		DBL.ShutDBConnection();
	}
	
	private void updateRanking(List<Profile> profileList,Map<Integer,List<Player>> finalStandings) {

		int j=0,i=0; 
		Integer[] listOfMoney = new Integer[6]; 
		finalStandings.keySet().toArray(listOfMoney); 
		int LosersRating = 0; 
		int WinnersRating = 0; 

		if (profileList.size()>1){ 
			Profile pro = null; 
			int counter = 0; 
			int[] points = new int[profileList.size()]; 

			if (profileList.size()%2 == 0){ 
				for(i = 0 ; i <  profileList.size()/2; i++){ 
					points[i] = ((profileList.size()/2)-i)*20; 
				} 
				for(i = profileList.size()-1 ; i >=  profileList.size()/2; i--){ 
					points[i] = ((profileList.size()/2)-1-i)*20; 
				} 
			} 
			else { 
				for(i = 0 ; i <  profileList.size(); i++){ 
					points[i] = ((profileList.size()/2)-i)*20; 
				} 
			} 

			int[] actualPoints = new int[finalStandings.size()]; 
			for(i = 0 ; i < finalStandings.size(); i++){ 
				for(j = 0; j < finalStandings.get(listOfMoney[i]).size(); j++){ 
					actualPoints[i] = actualPoints[i] + points[counter]; 
					counter++; 
				} 
			}        

			int numOfPlayers = profileList.size(); 
			Set<Profile> winners = new HashSet<Profile>();  
			Set<Profile> losers = new HashSet<Profile>();  
			int[] updatedRatings = new int[numOfPlayers]; 
			int k = 0; 
			int m = 0; 
			boolean bool = false; 

			for(Profile p : profileList){ 
				losers.add(p); 
			} 

			for (i = 0; i < actualPoints.length; i++){ 
				for (j = 0; j < finalStandings.get(listOfMoney[i]).size(); j++){ 

					Player curPlayer = finalStandings.get(listOfMoney[i]).get(j); 
					long curPlayerID = curPlayer.getUserID(); 

					for(Profile p : profileList) 
					{ 
						if(curPlayerID == p.getUserID())
						{ 
							pro = p; 
							if(winners.contains(pro)) winners.remove(pro); 
							if(losers.contains(pro)) losers.remove(pro); 
						} 
						else 
						{ 
							for (m = 0; m < finalStandings.get(listOfMoney[i]).size(); m++){ 
								if (finalStandings.get(listOfMoney[i]).get(m).getUserID() == p.getUserID()){ 
									bool = true; 
									break; 
								} 
							} 
							if(!winners.contains(p) && bool){ 
								losers.remove(p); 
								bool = false; 
							}        
						} 
					} 
					int losersum = 0; 
					int winnersum = 0; 
					for(Profile p : losers) 
					{ 
						losersum += p.getRating();       
					} 

					for(Profile p : winners) 
					{ 
						winnersum += p.getRating();      
					} 
					if (losers.size() != 0){ 
						LosersRating = losersum/losers.size(); 
					} 
					if (winners.size() != 0){ 
						WinnersRating = winnersum/winners.size(); 
					} 
					int NewRating = 0; 
					NewRating = Math.max(0, pro.getRating() + actualPoints[i]/finalStandings.get(listOfMoney[i]).size() - Math.min(((pro.getRating()-LosersRating)/20),5*losers.size()) + Math.max(((WinnersRating-pro.getRating())/20),(-5)*winners.size())); 
					updatedRatings[k] = NewRating; 
					k++; 


				} 
				int t = 0;  
				for(Profile prof : profileList){ 
					for (t = 0; t < finalStandings.get(listOfMoney[i]).size(); t++){ 
						if (finalStandings.get(listOfMoney[i]).get(t).getUserID() == prof.getUserID()){ 
							winners.add(prof); 
							break; 
						} 
					} 
				} 
			} 
			k = 0; 
			for (i = 0; i < actualPoints.length; i++) { 
				for (j = 0; j < finalStandings.get(listOfMoney[i]).size(); j++) { 

					Player curPlayer = finalStandings.get(listOfMoney[i]).get(j); 
					long curPlayerID = curPlayer.getUserID(); 

					for(Profile p : profileList) { 
						if (curPlayerID == p.getUserID()) 
							p.setRating(updatedRatings[k]);  
					} 
					k++; 
				} 
			} 
		} 
	} 


    public Map<Integer, List<Profile>> updateHallOfFame(
            Map<Integer, List<Profile>> hallOfFame,
            Map<Integer, List<Player>> finalStandings) {

        final int SIZE_OF_TABLE = 10;
        ReverseIntegerComparator comparator = new ReverseIntegerComparator();
        int minimumForHallOfFame = 0;
        int index = 0;

        index = 1;
        for (int cash : hallOfFame.keySet()) {
            index += hallOfFame.get(cash).size();
            if (index > SIZE_OF_TABLE) {
                minimumForHallOfFame = cash;
                break;
            }
        }

        for (int currentCash : finalStandings.keySet()) {
            if (minimumForHallOfFame < currentCash) {
                List<Profile> sameCashProfiles = new ArrayList<Profile>();
                for (Player player : finalStandings.get(currentCash)) {
                    Profile profile;
                    try {
                        profile = DBL.queryProfile(player.getUserID());
                        sameCashProfiles.add(profile);
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }

                if (hallOfFame.containsKey(currentCash)) {
                    hallOfFame.get(currentCash).addAll(sameCashProfiles);
                } else {
                    hallOfFame.put(currentCash, sameCashProfiles);
                }
            }
        }

        index = 0;
        Map<Integer, List<Profile>> newHOF = new TreeMap<Integer, List<Profile>>(comparator);
        for (int cash : hallOfFame.keySet()) {
            if (index + hallOfFame.get(cash).size() <= SIZE_OF_TABLE) {
                index += hallOfFame.get(cash).size();
                newHOF.put(cash, hallOfFame.get(cash));
            } else {
                List<Profile> currentCashProfiles = new ArrayList<Profile>();
                for (int i = 0; index < SIZE_OF_TABLE
                        && i < hallOfFame.get(cash).size(); i++, index++) {
                    currentCashProfiles.add(hallOfFame.get(cash).get(i));
                }
                newHOF.put(cash, currentCashProfiles);
                break;
            }
        }
        return newHOF;
    }
}
	
	

